﻿using System;
using Fetches.Fetch.Dto;
using Fetches.Fetch.Fetcher;
using Newtonsoft.Json;
using StackExchange.Redis;

namespace Fetches.Fetch.Scheduler
{
    public class RedisScheduler : AbstractScheduler
    {
        private string QueueKey = string.Empty;//Push\Poll
        private string SizeKey = string.Empty;//size
        private string DuplicateCheckKey = string.Empty;
        private string FetcherKey = string.Empty;
        private string InitialKey = string.Empty;
        private string SuccessKey = string.Empty;
        private string FailureKey = string.Empty;

        private ConnectionMultiplexer _redis;

        public override void Initialize(string config, long uniqueId)
        {
            _redis = ConnectionMultiplexer.Connect(config);
            QueueKey = "queue_" + uniqueId;
            SizeKey = "set_" + uniqueId;
            DuplicateCheckKey = "item_" + uniqueId;
            FetcherKey = "fetcher_" + uniqueId;
            InitialKey = "init_" + uniqueId;
            SuccessKey = "success_" + uniqueId;
            FailureKey = "failure_" + uniqueId;
        }

        public override Request Poll()
        {
            var db = _redis.GetDatabase();

            var message = db.ListRightPop(QueueKey);
            if (string.IsNullOrEmpty(message)) return null;

            db.StringDecrement(SizeKey);
            return JsonConvert.DeserializeObject<Request>(message);

        }

        protected override void PushWhenNoDuplicate(Request request)
        {
            var db = _redis.GetDatabase();
            db.ListLeftPush(QueueKey, JsonConvert.SerializeObject(request));
            db.StringIncrement(SizeKey);

        }

        public override void Ack(Request request, bool success)
        {
            var db = _redis.GetDatabase();
            db.StringIncrement(success ? SuccessKey : FailureKey);
        }

        public override bool IsFirstRun()
        {
            var db = _redis.GetDatabase();
            return db.StringIncrement(InitialKey) == 1;
        }

        public override int WorkingFetcherCount()
        {
            var db = _redis.GetDatabase();
            return Convert.ToInt32(db.StringGet(FetcherKey));
        }

        public override int RemainingRequestCount()
        {
            var db = _redis.GetDatabase();
            return Convert.ToInt32(db.StringGet(SizeKey));
        }

        public override FetchTaskProgress GetFetchProgress()
        {
            var db = _redis.GetDatabase();
            var progress = new FetchTaskProgress()
            {
                Remains = Convert.ToInt32(db.StringGet(SizeKey)),
                Status = IsFetchFinished() ? FetchTaskStatus.Solved : FetchTaskStatus.Processing,
                Success = Convert.ToInt32(db.StringGet(SuccessKey)),
                Failure = Convert.ToInt32(db.StringGet(FailureKey))
            };
            return progress;
        }

        public override void FetcherStatusReport(AbstractFetcher fetcher)
        {
            var db = _redis.GetDatabase();
            if (fetcher.Status == FetcherStatus.Idle)
            {
                db.StringDecrement(FetcherKey);
            }
            else
            {
                db.StringIncrement(FetcherKey);
            }

        }

        public override bool IsDuplicate(Request request)
        {
            var db = _redis.GetDatabase();
            var hash = request.Hash;
            if (db.SetAdd(DuplicateCheckKey, hash))
            {
                return false;
            }
            return true;
        }

        public override void ResetDuplicateCheck()
        {
            var db = _redis.GetDatabase();
            db.KeyDelete(DuplicateCheckKey);
            db.StringSet(SizeKey, 0);
        }

        public override void Dispose()
        {
            _redis.Dispose();
        }


    }
}
